#include "pch.hpp"
#include "MergedErrorModel.hpp"

using namespace Engine;
using namespace Engine::Edit;




MergedErrorModel::MergedErrorModel()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////

MergedErrorModel::~MergedErrorModel()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




void MergedErrorModel::add(ErrorModel::const_ptr model)
{
	for(auto i = m_models.begin(); i != m_models.end(); ++i)
		if(*i == model)
			return;

	verify(connect(model.get(), SIGNAL(modelAboutToBeReset()), this, SLOT(onBeginResetModel())));
	verify(connect(model.get(), SIGNAL(modelReset()), this, SLOT(onEndResetModel())));
	verify(connect(model.get(), SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)), SLOT(onBeginInsertRows(const QModelIndex&, int, int))));
	verify(connect(model.get(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(onEndInsertRows())));
	verify(connect(model.get(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), SLOT(onBeginRemoveRows(const QModelIndex&, int, int))));
	verify(connect(model.get(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(onEndRemoveRows())));

	if(model->rowCount() > 0)
	{
		beginInsertRows(QModelIndex(), rowCount(), rowCount() + model->rowCount() - 1);

		m_models.push_back(model);

		updateNumbers();

		endInsertRows();
	}
	else
	{
		// An empty model is added nevertheless, but we don't need to insert rows
		m_models.push_back(model);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void MergedErrorModel::remove(ErrorModel::const_ptr model)
{
	int count = 0;
	for(auto i = m_models.begin(); i != m_models.end(); ++i)
	{
		if(*i == model)
		{
			verify(disconnect(model.get(), SIGNAL(modelAboutToBeReset()), this, SLOT(onBeginResetModel())));
			verify(disconnect(model.get(), SIGNAL(modelReset()), this, SLOT(onEndResetModel())));
			verify(disconnect(model.get(), SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)), this, SLOT(onBeginInsertRows(const QModelIndex&, int, int))));
			verify(disconnect(model.get(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), this, SLOT(onEndInsertRows())));
			verify(disconnect(model.get(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), this, SLOT(onBeginRemoveRows(const QModelIndex&, int, int))));
			verify(disconnect(model.get(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(onEndRemoveRows())));

			if(model->rowCount() > 0)
			{
				beginRemoveRows(QModelIndex(), count, count + model->rowCount() - 1);

				m_models.erase(i);
				updateNumbers();

				endRemoveRows();
			}
			else
			{
				m_models.erase(i);
			}

			return;
		}
		else
		{
			count += (*i)->rowCount();
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int MergedErrorModel::rowCount(logger::entry::Level lvl) const
{
	auto i = m_counts.find(lvl);
	if(i != m_counts.end())
		return i->second;
	else
		return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




DocumentWindow* MergedErrorModel::window(const QModelIndex& index) const
{
	if(!index.isValid())
		return nullptr;

	if(index.row() >= rowCount() || index.column() >= columnCount())
		return nullptr;

	std::size_t count = 0;
	for(auto i = m_models.begin(); i != m_models.end(); ++i)
	{
		int relative = index.row() - count;
		int rowCount = (*i)->rowCount();

		if(relative >= 0 && relative < rowCount)
			return (*i)->documentWindow();

		count += rowCount;
	}

	return nullptr;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




int MergedErrorModel::rowCount(const QModelIndex& index) const
{
	if(index.isValid())
		return 0;

	int count = 0;
	for(auto i = m_models.begin(); i != m_models.end(); ++i)
		count += (*i)->rowCount();

	return count;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int MergedErrorModel::columnCount(const QModelIndex& index) const
{
	if(index.isValid())
		return 0;

	return 6;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QVariant MergedErrorModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	return ErrorModel::_headerData(section, orientation, role);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QVariant MergedErrorModel::data(const QModelIndex& index, int role) const
{
	int count = 0;
	for(auto i = m_models.begin(); i != m_models.end(); ++i)
	{
		auto lastCount = count;
		count += (*i)->rowCount();

		if(index.row() >= lastCount && index.row() < count)
		{
			auto row = index.row() - lastCount;
			return (*i)->data((*i)->index(row, index.column()), role);
		}
	}

	return QVariant();
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void MergedErrorModel::onBeginResetModel()
{
	auto model = dynamic_cast<ErrorModel*>(QObject::sender());
	verify(model != nullptr);
	verify(model->rowCount() > 0);

	onBeginRemoveRows(QModelIndex(), 0, model->rowCount() - 1);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void MergedErrorModel::onEndResetModel()
{
	updateNumbers();
	onEndRemoveRows();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void MergedErrorModel::onBeginInsertRows(const QModelIndex& parent, int first, int last)
{
	auto model = dynamic_cast<ErrorModel*>(QObject::sender());
	verify(model != nullptr);

	int offset = 0;
	for(auto i = m_models.begin(); i != m_models.end(); ++i)
	{
		if(i->get() == model)
		{
			beginInsertRows(parent, offset + first, offset + last);
			return;
		}

		// NEVER, EVER, EVER put cruical code inside the last statement of a for-loop!!!!
		// (offset was increased by the rowcount of the NEXT model, not the CURRENT)
		offset+=(*i)->rowCount();
	}

	// Maybe throw?
	throw programming_error("The sender is not part of the merged model");
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void MergedErrorModel::onEndInsertRows()
{
	updateNumbers();
	endInsertRows();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void MergedErrorModel::onBeginRemoveRows(const QModelIndex& parent, int first, int last)
{
	auto model = dynamic_cast<ErrorModel*>(QObject::sender());
	verify(model != nullptr);

	int offset = 0;
	for(auto i = m_models.begin(); i != m_models.end(); ++i)
	{
		if(i->get() == model)
		{
			beginRemoveRows(parent, offset + first, offset + last);
			return;
		}

		// NEVER, EVER, EVER put cruical code inside the last statement of a for-loop!!!!
		// (offset was increased by the rowcount of the NEXT model, not the CURRENT)
		offset += (*i)->rowCount();
	}

	// Maybe throw?
	throw programming_error("The sender is not part of the merged model");
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void MergedErrorModel::onEndRemoveRows()
{
	updateNumbers();
	endRemoveRows();
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void MergedErrorModel::updateNumbers()
{
	for(int n = 0; n < logger::entry::num_levels; ++n)
	{
		auto lvl = static_cast<logger::entry::Level>(n);
		auto i = m_counts.find(lvl);
		if(i == m_counts.end())
			i = m_counts.insert(std::make_pair(lvl, 0)).first;
		else
			i->second = 0;

		foreach(const auto& model, m_models)
		{
			int count = model->rowCount(lvl);

			if(i == m_counts.end())
					m_counts.insert(std::make_pair(lvl, count));
			else
				i->second += count;
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
